#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define Convert(addrptr) ((struct sockaddr *)addrptr)

namespace NetWorkModule
{
    const static int defaultsockfd = -1;
    const int backlog = 5;

    enum
    {

        SocketError = 1,
        BindError,
        ListenError
    };
    // 封装一个基类，Socket接口类
    // 设计模式：模版方法类
    class Socket
    {
    public:
        virtual ~Socket() {}
        virtual void CreateSocketOrDie() = 0;
        virtual void BindSocketOrDie(uint16_t port) = 0;
        virtual void ListenSocketOrDie(int backlog) = 0;
        virtual int AcceptConnection(std::string *peerip, uint16_t *peerport) = 0;   //接受连接
        virtual bool ConnectServer(std::string &serverip, uint16_t serverport) = 0;  //连接服务器
        virtual int GetSockFd() = 0;    //获取文件描述符
        virtual void SetSockFd(int sockfd) = 0;     //设置
        virtual void CloseSocket() = 0;
        virtual bool Recv(std::string *buffer, int size) = 0;
        virtual void Send(std::string &send_str) = 0;
        virtual void ReUseAddr() = 0;
        // TODO
        public:
        void BulidListenSocketMethod(uint16_t port,int backlog)
        {
            CreateSocketOrDie();
            ReUseAddr();
            BindSocketOrDie(port);
            ListenSocketOrDie(backlog);
        }
        bool BuildConnectSocketMethod(std::string &serverip,uint16_t serverport)
        {
            CreateSocketOrDie();
            return ConnectServer(serverip,serverport);
        }
        void BuildNormalSocketMethod(int sockfd)
        {
            SetSockFd(sockfd);
        }
    };

    class TcpSocket:public Socket
    {
    public:
    TcpSocket(int sockfd=defaultsockfd):_sockfd(sockfd)
    {

    }
    ~TcpSocket()
    {

    }
    void CreateSocketOrDie() override
    {
        _sockfd=::socket(AF_INET,SOCK_STREAM,0);
        if(_sockfd<0)
        exit(SocketError);
    }
    void BindSocketOrDie(uint16_t port) override
    {
        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_family=AF_INET;
        local.sin_addr.s_addr=INADDR_ANY;
        local.sin_port=htons(port);
        int n=::bind(_sockfd,Convert(&local),sizeof(local));
        if(n<0)
        exit(BindError);
    }

    void ListenSocketOrDie(int backlog) override
    {
        int n=::listen(_sockfd,backlog);
        if(n<0)
        exit(ListenError);
    }
    int AcceptConnection(std::string *peerip,uint16_t *peerport) override
    {
        struct sockaddr_in peer;
        socklen_t len=sizeof(peer);
        int newsockfd=::accept(_sockfd,Convert(&peer),&len);
        if(newsockfd<0)
        return -1;   //连接失败
        *peerport=ntohs(peer.sin_port);
        *peerip=inet_ntoa(peer.sin_addr);
        return newsockfd;
    }

    bool ConnectServer(std::string &serverip, uint16_t serverport) override
    {
        struct sockaddr_in server;
        memset(&server,0,sizeof(server));
        server.sin_addr.s_addr=inet_addr(serverip.c_str());
        server.sin_port=htons(serverport);
        server.sin_family=AF_INET;

        int n=::connect(_sockfd,Convert(&server),sizeof(server));
        if(n==0)
        return true;
        else
        return false;
    }

    int GetSockFd() override
    {
        return _sockfd;
    }
    void  SetSockFd(int sockfd) override
    {
        _sockfd=sockfd;
    }
    void CloseSocket() override
    {
        if (_sockfd > defaultsockfd)
            ::close(_sockfd);
    }
    bool Recv(std::string *buffer,int size) override
    {
        char inbuffer[size];
        ssize_t n=recv(_sockfd,inbuffer,size-1,0);
        if(n>0)
        {
            inbuffer[size]=0;
            *buffer+=inbuffer;
            return true;
        }
        else if(n==0)
        {
            return false;
        }
        else
        {
            return false;
        }
    }
    void Send(std::string &sendstr)override
    {
        ssize_t n=::send(_sockfd,sendstr.c_str(),sendstr.size(),0);
    }
    void ReUseAddr() override
    {
        int opt=1;
        setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    }
 
    private:
        int _sockfd;
    };

}